package com.hmall.trade.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.cart.po.OrderList;
import com.hmall.api.item.ItemClient;
import com.hmall.api.item.dto.ItemDTO;
import com.hmall.api.item.dto.OrderDetailDTO;
import com.hmall.common.constant.MqConstants;
import com.hmall.common.exception.BadRequestException;
import com.hmall.common.utils.UserContext;
import com.hmall.trade.domain.dto.OrderFormDTO;
import com.hmall.api.trade.po.Order;
import com.hmall.trade.domain.po.OrderDetail;
import com.hmall.trade.mapper.OrderMapper;
import com.hmall.trade.service.IOrderDetailService;
import com.hmall.trade.service.IOrderService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2023-05-05
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

	private final ItemClient itemClient;
	private final IOrderDetailService detailService;
	// private final CartClient cartClient;

	private final AmqpTemplate amqpTemplate;

	@Override
	// @Transactional
	@GlobalTransactional
	public Long createOrder(OrderFormDTO orderFormDTO) {
		Long userId = UserContext.getUser();

		// 1.订单数据
		Order order = new Order();
		// 1.1 查询商品
		List<OrderDetailDTO> detailDTOS = orderFormDTO.getDetails();
		// 1.2 获取商品 id 和数量的 Map
		Map<Long, Integer> itemNumMap = detailDTOS.stream().collect(Collectors.toMap(OrderDetailDTO::getItemId,
			OrderDetailDTO::getNum
		));
		Set<Long> itemIds = itemNumMap.keySet();
		// 1.3.查询商品
		List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
		if (items == null || items.size() < itemIds.size()) {
			throw new BadRequestException("商品不存在");
		}
		// 1.4.基于商品价格、购买数量计算商品总价：totalFee
		int total = 0;
		for (ItemDTO item : items) {
			total += item.getPrice() * itemNumMap.get(item.getId());
		}
		order.setTotalFee(total);
		// 1.5.其它属性
		order.setPaymentType(orderFormDTO.getPaymentType());
		order.setUserId(userId);
		order.setStatus(1);
		// 1.6.将Order写入数据库order表中
		save(order);

		// 2.保存订单详情
		List<OrderDetail> details = buildDetails(order.getId(), items, itemNumMap);
		detailService.saveBatch(details);

		/*  3.清理购物车商品
		cartService.removeByItemIds(itemIds);
		cartClient.deleteCartItemByIds(userId, itemIds.stream().sorted().collect(Collectors.toList())); */
		OrderList orderList = new OrderList();
		orderList.setUserId(userId);
		orderList.setOrderList(itemIds.stream().sorted().collect(Collectors.toList()));
		amqpTemplate.convertAndSend(MqConstants.TRADE_EXCHANGE_NAME, MqConstants.ORDER_CREATE_KEY, orderList);

		// 4.扣减库存
		try {
			itemClient.deductStock(detailDTOS);
		} catch (Exception e) {
			throw new RuntimeException("库存不足！");
		}
		// int i = 1/0;

		/*
		 * 发送消息，延时更新未支付订单状态
		 *
		 * MessageBuilder.withBody：创建消息对象，并将 订单ID 写入消息体中
		 * amqpTemplate.convertAndSend：发送消息，将信息发送到指定交换机和路由键
		 *     参数一：交换机名称
		 *     参数二：路由键
		 *     参数三：消息对象
		 *
		 * 将信息发送到 trade.pay.direct 普通交换机，路由键为 dead.ttl（死信交换机对应的路由键），消息体为订单ID。当传递到交换机后，通过 dead.ttl 路由键，可以将转换器中的信息通过队列传递到死信交换机中，再由死信队列进行处理
		 */
		Message message = MessageBuilder.withBody(order.getId().toString().getBytes(StandardCharsets.UTF_8)).build();
		amqpTemplate.convertAndSend("trade.pay.direct", "dead.ttl", message);
		log.warn("下单成功！！");

		return order.getId();
	}

	@Override
	public void markOrderPaySuccess(Long orderId) {
		// Order order = new Order();
		// order.setId(orderId);
		// order.setStatus(2);
		// order.setPayTime(LocalDateTime.now());
		// updateById(order);

		// 未支付的订单才能被改成已支付
		this.lambdaUpdate()
			.eq(Order::getId, orderId)
			.eq(Order::getStatus, 1)
			.set(Order::getStatus, 2)
			.set(Order::getPayTime, LocalDateTime.now())
			.update();
	}

	private List<OrderDetail> buildDetails(Long orderId, List<ItemDTO> items, Map<Long, Integer> numMap) {
		List<OrderDetail> details = new ArrayList<>(items.size());
		for (ItemDTO item : items) {
			OrderDetail detail = new OrderDetail();
			detail.setName(item.getName());
			detail.setSpec(item.getSpec());
			detail.setPrice(item.getPrice());
			detail.setNum(numMap.get(item.getId()));
			detail.setItemId(item.getId());
			detail.setImage(item.getImage());
			detail.setOrderId(orderId);
			details.add(detail);
		}
		return details;
	}
}
